﻿Imports System.Runtime.CompilerServices
Imports System.Numerics
Namespace Math

  <HideModuleName()>
  Public Module BigDecimal_Exts
    <Extension()>
    Private Function ConsCont(ByVal X As BigDecimal) As BigDecimal
      Console.Write("{0}[{1}]", X.Digits, X.DecimalPoint)
      Return X
    End Function
    <Extension()>
    Friend Sub ChangeSign(ByRef X As BigDecimal)
      If X.Digits.Sign = 0 Then Exit Sub
      X.Digits *= BigInteger.MinusOne
    End Sub

    <Extension()>
    Public Function Pow(ByVal X As BigInteger, ByVal y As BigInteger) As BigInteger
      If y < 0 Then Throw New NotSupportedException
      Dim RX = BigDecimals.BigDecimal.CloneObject(X)
      If y = 0 Then Return Numerics.BigInteger.One
      If y = 1 Then Return RX
      If y = 2 Then Return RX * RX
      Dim TX As BigInteger
      If y Mod 2 = 0 Then
        TX = Pow(X, y / 2)
        Return TX * TX
      End If
      TX = Pow(X, y / 2)
      Return TX * TX * RX
    End Function

 
    <Extension()>
    Public Function Floor(ByVal X As BigDecimal) As BigDecimal
      If X.IsNegative Then
        Return (X + FPart(X)) - 1
      Else
        Return X - FPart(X)
      End If
    End Function
      <Extension()>
      Public Function Ceiling(ByVal X As BigDecimal) As BigDecimal
        If X.IsNegative Then
          Return (X + FPart(X))
        Else
          Return (X - FPart(X)) + 1
        End If
      End Function

      <Extension()>
      Public Function IPart(ByVal X As BigDecimal) As BigDecimal
        Return X + If(X.IsNegative, -FPart(X), FPart(X))
      End Function
      <Extension()>
      Public Function FPart(ByVal X As BigDecimal) As BigDecimal
        Return If(X.IsNegative, -X, X) Mod 1
      End Function
      <Extension()>
      Friend Function Increment(ByVal BDec As BigDecimal) As BigDecimal
        If BDec.IsntNull Then
          Return BDec + 1
        Else
          Throw New ArgumentNullException("Abs: BDec is null")
        End If
      End Function
      <Extension()>
      Friend Function Decrement(ByVal BDec As BigDecimal) As BigDecimal
        If BDec.IsntNull Then
          Return BDec - 1
        Else
          Throw New ArgumentNullException("Abs: BDec is null")
        End If
      End Function

      <Extension()>
      Public Function Abs(ByVal BDec As BigDecimal) As BigDecimal
        If BDec.IsntNull Then
          If BDec.IsNegative Then Return BigDecimal.CreateNew(BigInteger.Abs(BDec.Digits), CUInt(BDec.DecimalPoint))
          Return BDec
        Else
          Throw New ArgumentNullException("Abs: BDec is null")
        End If
      End Function

      <Extension()>
      Public Function Recipricol(ByVal X As BigDecimal) As BigDecimal
        If X.IsntNull Then
          If X = 0 Then Throw New DivideByZeroException()
          Return BigDecimal.One / X
        Else
          Throw New ArgumentNullException("Recipricol: X is null")
        End If
      End Function

      ''' <summary>
      ''' GCD
      ''' </summary>
      ''' <param name="n"></param>
      ''' <param name="m"></param>
      ''' <returns>The Greatest Common Divisor</returns>
      ''' <remarks></remarks>
      <Extension()>
      Public Function GCD(ByVal n As BigDecimal, ByVal m As BigDecimal) As BigDecimal
        If n.IsntNull Then
          If m.IsntNull Then
            If n = m Then
              Return n
            ElseIf n > m Then
              Return GCD(n - m, m)
            Else
              Return GCD(m, m - n)
            End If
          End If
          Throw New ArgumentNullException("GCD: m is null")
        Else
          Throw New ArgumentNullException("GCD: n is null")
        End If
      End Function

      <Extension()>
      Public Function IsNegative(ByVal BDec As BigDecimal) As Boolean
        If BDec.IsntNull Then Return BDec.Digits.Sign = -1
        Return False
      End Function


    End Module
End Namespace

Public Module Op_Exts
  ''' <summary>
  ''' 
  ''' </summary>
  ''' <param name="BDec">Align this BigDecimal</param>
  ''' <param name="ToBDec">Toward this other BigDecimal</param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  <Extension()>
  Public Function AlignedTo(ByVal BDec As BigDecimal, ByVal ToBDec As BigDecimal) As BigDecimal
    If BDec.IsntNull AndAlso ToBDec.IsntNull Then
      Return New BigDecimal With {
       .Digits = BigInteger.Multiply(
        BDec.Digits,
        BigInteger.Pow(CType(10, BigInteger),
                       CInt(ToBDec.DecimalPoint - BDec.DecimalPoint)
                      )
      ),
      .DecimalPoint = ToBDec.DecimalPoint}
    Else
      Throw New ArgumentNullException
    End If
  End Function

End Module